/* 
* sapdp-core.c
*
* Copyright (c) 2010, Zachary N J Peterson <znpeters@nps.edu>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*     * Redistributions of source code must retain the above copyright
*       notice, this list of conditions and the following disclaimer.
*     * Redistributions in binary form must reproduce the above copyright
*       notice, this list of conditions and the following disclaimer in the
*       documentation and/or other materials provided with the distribution.
*     * Neither the name of the Naval Postgraduate School nor the
*       names of its contributors may be used to endorse or promote products
*       derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY ZACHARY N J PETERSON ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ZACHARY N J PETERSON BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include "sapdp.h"

SAPDP_client_tag *sapdp_client_tag_block(SAPDP_key *key, unsigned char *block, size_t blocksize, unsigned int index){

	SAPDP_client_tag *tag = NULL;
	BIGNUM *message = NULL;
	BIGNUM *temp = NULL;
	BN_CTX * ctx = NULL;
	struct timeval tv1, tv2;
	
	if(!key || !block || !blocksize) return NULL;

	/* Allocate memory */
	if( ((tag = generate_sapdp_client_tag()) == NULL)) return NULL;
	if( ((ctx = BN_CTX_new()) == NULL)) goto cleanup;

	/* Pick some random values from Z*n */
#ifdef USE_FDPRF
	if(!BN_rand_range(tag->r, key->rsa->n)) goto cleanup;
	// Add one just in case we hit the 1/n chance we got 0.
	if(!BN_add(tag->r, tag->r, BN_value_one())) goto cleanup;
#else
	if(!BN_rand_range(tag->r1, key->rsa->n)) goto cleanup;
	if(!BN_rand_range(tag->r2, key->rsa->n)) goto cleanup;
	if(!BN_add(tag->r1, tag->r1, BN_value_one())) goto cleanup;
	if(!BN_add(tag->r2, tag->r2, BN_value_one())) goto cleanup;
#endif

	/* Turn the data block into a BIGNUM */
	message = BN_bin2bn(block, blocksize, NULL);
	if(!message) goto cleanup;

	gettimeofday(&tv1, NULL);

	/* Reduce the message by modulo N to compute b*/
	if(!BN_mod(tag->b, message, key->rsa->n, ctx)) goto cleanup;	

			
	/* Compute e = (F - b)/N */
//	if(!BN_sub(tag->e, message, tag->b)) goto cleanup;
	if(!BN_mod_sub(tag->e, message, tag->b, key->rsa->n, ctx)) goto cleanup;
//	if(!BN_div(tag->e, NULL, tag->e, key->rsa->n, ctx)) goto cleanup;
	if(!BN_div(tag->e, NULL, tag->b, key->rsa->n, ctx)) goto cleanup;
	
	/* Compute er + f_k(i)  or
	 * Compute er1 + r2 
	 */
#ifdef USE_FDPRF
//	if(!BN_mul(tag->z, tag->r, tag->e, ctx)) goto cleanup;
	if(!BN_mod_mul(tag->z, tag->r, tag->e, key->rsa->n, ctx)) goto cleanup;
	temp = generate_fdprf_f(key, (unsigned char *)&index, sizeof(unsigned int));
//	if(!BN_add(tag->z, tag->z, temp)) goto cleanup;	
	if(!BN_mod_add(tag->z, tag->z, temp, key->rsa->n, ctx)) goto cleanup;	
#else
//	if(!BN_mul(tag->z, tag->r1, tag->e, ctx)) goto cleanup;
//	if(!BN_add(tag->z, tag->z, tag->r2)) goto cleanup;	
	if(!BN_mod_mul(tag->z, tag->r1, tag->e, key->rsa->n, ctx)) goto cleanup;
	if(!BN_mod_add(tag->z, tag->z, tag->r2, key->rsa->n, ctx)) goto cleanup;		
#endif

	gettimeofday(&tv2, NULL);
	printf("\tSAPDP: %lf\n", (double)( (double)(double)(((double)tv2.tv_sec) + (double)((double)tv2.tv_usec/1000000)) - (double)((double)((double)tv1.tv_sec) + (double)((double)tv1.tv_usec/1000000)) ) );


	if(message) BN_clear_free(message);	
	if(temp) BN_clear_free(temp);
	if(ctx) BN_CTX_free(ctx);
	
	return tag;
		
cleanup:
	if(tag) destroy_sapdp_client_tag(tag);
	if(message) BN_clear_free(message);
	if(temp) BN_clear_free(temp);
	if(ctx) BN_CTX_free(ctx);
	
	return NULL;
}

SAPDP_server_tag *sapdp_server_tag_block(SAPDP_key *key, SAPDP_client_tag *ctag){

	SAPDP_server_tag *tag = NULL;
	BN_CTX * ctx = NULL;
	
	if(!key || !ctag) return NULL;
	
	/* Allocate memory */
	if( ((tag = generate_sapdp_server_tag()) == NULL)) goto cleanup;
	if( ((ctx = BN_CTX_new()) == NULL)) goto cleanup;
	
	/* Compute x */
	//if(!BN_exp(tag->x, key->g, ctag->e, ctx)) goto cleanup;
	if(!BN_mod_exp(tag->x, key->g, ctag->e, key->rsa->n, ctx)) goto cleanup;	
	
	/* Compute y */
	//if(!BN_exp(tag->y, key->g, ctag->z, ctx)) goto cleanup;
	if(!BN_mod_exp(tag->y, key->g, ctag->z, key->rsa->n, ctx)) goto cleanup;

	if(ctx) BN_CTX_free(ctx);
	
	return tag;
		
cleanup:
	if(tag) destroy_sapdp_server_tag(tag);
	if(ctx) BN_CTX_free(ctx);
	
	return NULL;
}

DPDP_tag *dpdp_tag_block(DPDP_key *key, unsigned char *block, size_t blocksize){

	DPDP_tag *tag = NULL;
	BN_CTX * ctx = NULL;
	BIGNUM *message = NULL;
	BIGNUM *r0 = NULL;
	BIGNUM *r1 = NULL;
	BIGNUM *phi = NULL;
	struct timeval tv1, tv2;
	
	if(!key || !block || !blocksize) return NULL;
	
		/* Allocate memory */
	if( ((tag = generate_dpdp_tag()) == NULL)) goto cleanup;
	if( ((ctx = BN_CTX_new()) == NULL)) goto cleanup;
	if( ((phi = BN_new()) == NULL)) goto cleanup;
	if( ((r0 = BN_new()) == NULL)) goto cleanup;
	if( ((r1 = BN_new()) == NULL)) goto cleanup;

	/* Turn the data block into a BIGNUM */
	message = BN_bin2bn(block, blocksize, NULL);
	if(!message) goto cleanup;

	gettimeofday(&tv1, NULL);	
	/* Calculate phi */
	if (!BN_sub(r0, key->rsa->p, BN_value_one())) goto cleanup;	/* p-1 */
	if (!BN_sub(r1, key->rsa->q, BN_value_one())) goto cleanup;	/* q-1 */
	if (!BN_mul(phi, r0, r1, ctx)) goto cleanup;	/* phi = (p-1)(q-1) */

	
	/* Reduce the message by mod phi(N) */
	if(!BN_mod(message, message, phi, ctx)) goto cleanup;


	/* Compute g^m mod N */
	if(!BN_mod_exp(tag->M, key->g, message, key->rsa->n, ctx)) goto cleanup;	
	gettimeofday(&tv2, NULL);
	printf("\tDPDP: %lf\n", (double)( (double)(double)(((double)tv2.tv_sec) + (double)((double)tv2.tv_usec/1000000)) - (double)((double)((double)tv1.tv_sec) + (double)((double)tv1.tv_usec/1000000)) ) );



	if(ctx) BN_CTX_free(ctx);
	if(message) BN_clear_free(message);
	if(phi) BN_clear_free(phi);
	if(r0) BN_clear_free(r0);
	if(r1) BN_clear_free(r1);
	
	return tag;
		
cleanup:
	if(tag) destroy_dpdp_tag(tag);
	if(message) BN_clear_free(message);
	if(phi) BN_clear_free(phi);
	if(r0) BN_clear_free(r0);
	if(r1) BN_clear_free(r1);
	if(ctx) BN_CTX_free(ctx);
	
	return NULL;
	
}